package ru.nosport.matrixaria.core;

import org.apache.commons.configuration.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * User: vfabr
 * Date: 25.09.2006
 * Time: 13:33:23
 * 
 */
class InitConfiguration {
	private List<ModuleShortcut> modulesOrdered;
	private Map<String, String> moduleClasses;
	private Map<String, ModuleShortcut> modules;
	private HierarchicalConfiguration conf;
	private CoreVars vars;

	/**
	 *
	 * @param _conf
	 * @param _part
	 * @throws InitException
	 * @throws ConfigurationException
	 * @throws CoreException
	 */
	public InitConfiguration(HierarchicalConfiguration _conf, String _part) throws InitException, ConfigurationException, CoreException {
		this.vars = CoreVars.getInstance();
		AbstractConfiguration.setDefaultListDelimiter(vars.charConfDelimiter);
		this.modulesOrdered = new ArrayList<ModuleShortcut>(vars.initialRegSize);
		this.moduleClasses = new HashMap<String, String>(vars.initialRegSize);
		this.modules = new HashMap<String, ModuleShortcut>(vars.initialRegSize);
		this.conf = _conf;

		this.loadModuleConfs(_part);
		this.checkDependences();
	}

	/**
	 * Startup loading
	 *
	 * @throws ConfigurationException
	 */
	private void loadModuleConfs(String _part) throws ConfigurationException, InitException {
		int size = this.conf.getMaxIndex("module");
		for (int i = 0; i <= size; i++) {
			String path = this.conf.getString("module(" + i + ")[@class]");
			this.crtModuleShortcut(
					new XMLConfiguration(_part + "/" + path + ".xml"),
					path);
		}
	}

	/**
	 * @param _module
	 * @param _className
	 */
	private void crtModuleShortcut(HierarchicalConfiguration _module, String _className) throws InitException {
		int size = _module.getMaxIndex("instance");
		for (int i = 0; i <= size; i++) {
			if (_module.getBoolean("instance(" + i + ")[@autorun]")) {
				this.modules.put(
						_module.getString("instance(" + i + ")[@name]"),
						new ModuleShortcut(
								_module.configurationAt("instance(" + i + ")"),
								_className)
				);
				this.moduleClasses.put(_module.getString("instance(" + i + ")[@name]"), _className);
			}
		}
	}

	/**
	 *
	 */
	private void checkDependences() throws InitException {
		Map<String, Map> unchecked = new HashMap<String, Map>(this.modules.size());
		Map<String, Map> checked = new HashMap<String, Map>(this.modules.size());

		for (ModuleShortcut module : this.modules.values()) {
			unchecked.put(module.getInstanceName(), module.getDependences());
		}

		String[] names;
		names = new String[unchecked.keySet().size()];
		names = unchecked.keySet().toArray(names);

		while (names.length > 0) {
			this.drillDependence(names[0], 0, unchecked, checked);
			names = new String[unchecked.keySet().size()];
			names = unchecked.keySet().toArray(names);
		}

	}

	/**
	 * @param _name
	 * @param _depth
	 * @param _unchecked
	 * @param _checked
	 * @throws InitException
	 */
	private void drillDependence(String _name, int _depth, Map<String, Map> _unchecked, Map<String, Map> _checked) throws InitException {
		if (_depth > this.vars.dependenceDepth) {
			throw new InitException("Dependence depth [" + this.vars.dependenceDepth + "] exeeded on module instance " + _name + ", module '" + this.moduleClasses.get(_name) + "' probable dependence loop detection.");
		} else if (_unchecked.containsKey(_name)) {
			Map<String, String> deps = _unchecked.get(_name);
			for (String _depName : deps.keySet()) {
				if (! deps.get(_depName).equals(this.moduleClasses.get(_depName))) {
					throw new InitException("Instance '" + _depName + "' belongs to module '" +
							this.moduleClasses.get(_depName) + "' instead of required '" + deps.get(_depName)
							+ "' (by dependence from instance '" + _name + "', module '" + this.moduleClasses.get(_name) + "')");
				}
				this.drillDependence(_depName, _depth + 1, _unchecked, _checked);
			}
			this.moveCheckedModule(_name, _unchecked, _checked);
			this.buildOrder(_name);
		} else if (_checked.containsKey(_name)) {

		} else {
			throw new InitException("Module's " + _name + " config is not found");
		}
	}

	/**
	 * 
	 * @param _name
	 */
	private void buildOrder(String _name) {
		this.modulesOrdered.add(this.modules.get(_name));
	}

	/**
	 * Moves an object (&lt;Map&gt;) from one set to another
	 *
	 * @param _name	  Name of the moved object
	 * @param _unchecked Source
	 * @param _checked   Destination
	 */
	private void moveCheckedModule(String _name, Map<String, Map> _unchecked, Map<String, Map> _checked) {
		_checked.put(_name, _unchecked.remove(_name));
	}

	/**
	 * @return List
	 */
	public List<ModuleShortcut> getModuleShortcutList() {
		return modulesOrdered;
	}
}
